<?php

namespace App\Services;

use App\Common\Libs\ArrayHelper;
use App\Common\Libs\Dispatch\Queue;
use App\Common\Libs\IdBuilder;
use App\Common\Libs\Json;
use App\Consts\GlobalConst;
use App\Consts\GlobalCounterConst;
use App\Consts\GlobalNoteConst;
use App\Consts\GlobalQueueConst;
use App\Formats\NoteFormat;
use App\Formats\TopicFormat;
use App\Jobs\Controller\NoteJob;
use App\Models\Cache\Cache;
use App\Models\Cache\CacheKey;
use App\Models\Field\ArticleField;
use App\Models\Field\UserField;
use App\Models\IndexNote;
use App\Models\Select\NoteSelect;
use App\Models\SysNote;
use App\Models\SysNoteContent;
use App\Models\SysNoteImg;

/**
 * @package App\Services
 * @see NoteService
 */
class NoteService extends BaseService
{

    /**
     * @var \App\Repository\NoteRepository
     */
    private $_repository;

    /**
     * @var $_note
     */
    private $_note;

    /**
     * NoteService constructor.
     */
    public function __construct()
    {
        parent::__construct();
        $this->_repository = repository()->NoteRepository;
    }

    public function choiceList($searchParams)
    {
        $provider = IndexNote::getInstance()
            ->where('article_id', $searchParams['article_id'])
            ->where('is_public', GlobalNoteConst::IS_PUBLIC)
            ->where('is_deleted', GlobalConst::IS_NOT_DELETE)
            ->where('is_shield', GlobalNoteConst::IS_NOT_SHIELD)
            ->where('is_view', GlobalNoteConst::IS_VIEW);

        $provider->orWhere(function ($query) use ($searchParams) {
            $query->where('is_deleted', GlobalConst::IS_NOT_DELETE)
                ->where('user_id', $searchParams['current_user_id'])
                ->where('is_shield', GlobalNoteConst::IS_NOT_SHIELD)
                ->where('is_view', GlobalNoteConst::IS_VIEW)
                ->where('article_id', $searchParams['article_id']);
        });

        $provider->orderByDesc('created_at');

        $indexNoteArr = $provider->get();

        $noteList = [];
        foreach (ArrayHelper::getColumn($indexNoteArr, 'note_id') as $noteId) {
            $count = service()->CounterService->getTargetCount(CacheKey::COUNTER_NOTE, [
                'comment',
                'like',
                'collect'
            ], ['note_id' => $noteId]);
            $noteList[] = [
                'note_id' => $noteId,
                'comment' => $count['comment'],
                'like' => $count['like']
            ];
        }
        $noteList = ArrayHelper::multiSort($noteList, ['like', 'comment'], [SORT_DESC, SORT_DESC]);
        $noteList = array_slice($noteList, 0, 3);
        if (!$noteList) {
            $noteList = array_slice(ArrayHelper::toArray($indexNoteArr), 0, 3);
        }

        $list = formats()->NoteFormat->formatNoteList(ArrayHelper::getColumn($noteList, 'note_id'), $searchParams['current_user_id'], NoteFormat::SELF_LIST);

        $list = ArrayHelper::multiSort($list, ['count.like', 'count.comment', 'created_at'], [SORT_DESC, SORT_DESC, SORT_DESC]);

        $list = array_slice($list, 0, 3);

        return [
            'have_more' => false,
            'list' => $list,
        ];
    }

    public function lineNoteList($searchParams)
    {
        $dataProvider = $this->_repository->getLineNoteList($searchParams);
        $pagination = $this->getPagination();
        $indexNoteArr = $dataProvider->get();

        $list = formats()->NoteFormat->formatNoteList(ArrayHelper::getColumn($indexNoteArr, 'note_id'), $searchParams['current_user_id'], NoteFormat::SELF_LIST);

        $order = SORT_DESC;
        if (array_key_exists('order', $searchParams) && $searchParams['order'] == GlobalConst::ORDER_TYPE_ASC) {
            $order = SORT_ASC;
        }

        // 排序
        $list = ArrayHelper::multiSort($list, ['created_at'], $order);

        return self::outputMoreList($dataProvider, $pagination, $list);
    }

    public function searchNoteList($searchParams)
    {
        $dataProvider = $this->_repository->getSearchNoteList($searchParams);
        $pagination = $this->getPagination();
        $indexNoteArr = $dataProvider->get();

        $list = formats()->NoteFormat->formatNoteList(ArrayHelper::getColumn($indexNoteArr, 'note_id'), $searchParams['current_user_id'], NoteFormat::SEARCH_LIST);

        // 排序
        $list = ArrayHelper::multiSort($list, ['created_at', 'id'], [SORT_DESC, SORT_DESC]);

        return self::outputMoreList($dataProvider, $pagination, $list);
    }

    public function articleNoteList($searchParams)
    {
        $dataProvider = $this->_repository->getArticleNoteList($searchParams);
        $pagination = $this->getPagination();
        $indexNoteArr = $dataProvider->get();

        $list = formats()->NoteFormat->formatNoteList(ArrayHelper::getColumn($indexNoteArr, 'note_id'), $searchParams['current_user_id'], NoteFormat::SELF_LIST);

        // 排序
        $list = ArrayHelper::multiSort($list, ['created_at'], $searchParams['order']);

        return self::outputMoreList($dataProvider, $pagination, $list);
    }

    /**
     * @param $searchParams
     * @param array $selects
     * @return array
     */
    public function userNoteList($searchParams)
    {
        $dataProvider = $this->_repository->getUserNoteList($searchParams);
        $pagination = $this->getPagination();
        $indexNoteArr = $dataProvider->get();

        $list = formats()->NoteFormat->formatNoteList(ArrayHelper::getColumn($indexNoteArr, 'note_id'), $searchParams['current_user_id'], NoteFormat::SELF_LIST);

        $order = SORT_DESC;
        if (array_key_exists('order', $searchParams) && $searchParams['order'] == GlobalConst::ORDER_TYPE_ASC) {
            $order = SORT_ASC;
        }

        // 排序
        $list = ArrayHelper::multiSort($list, ['created_at'], $order);

        return self::outputMoreList($dataProvider, $pagination, $list);
    }

    /**
     * @param $params
     * @return \App\Models\SysNote|bool|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function createNote($params)
    {
        $params = ArrayHelper::merge($params, [
            'note_id' => IdBuilder::getUniqueID(),
        ]);

        $note = $this->_repository->create($params);
        if (!$note) {
            return false;
        }

        // 【暂时】新增索引记录
        $this->_repository->createIndex([
            'note_id' => $note->note_id,
            'article_id' => $note->article_id,
            'user_id' => $note->user_id,
            'start_index' => $params['start_index'] ?? 0,
            'end_index' => $params['end_index'] ?? 0,
            'type' => $note->type,
            'content' => $params['content'],
            'is_public' => $note->is_public,
        ]);

        return $note;
    }

    /**
     * @param $params
     * @return \App\Models\SysNoteContent|bool|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function createNoteContent($params)
    {
        return $this->_repository->createNoteContent($params);
    }

    /**
     * @param $params
     * @return \App\Models\SysNoteSelected|bool|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function createNoteSelected($params)
    {
        return $this->_repository->createNoteSelected($params);
    }

    /**
     * @param $params
     * @return \App\Models\SysNoteLine|bool|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function createNoteLine($params)
    {
        return $this->_repository->createNoteLine($params);
    }

    /**
     * @param $noteId
     * @return \App\Models\SysNote|bool|\Illuminate\Database\Eloquent\Builder|object|null
     */
    public function delNote($noteId)
    {
        return $this->_repository->delNoteById($noteId);
    }

    /**
     * @param $params
     * @return \App\Models\SysNote|\Illuminate\Database\Eloquent\Builder|object|null
     */
    public function editNote($params)
    {
        $note = $this->_repository->getNoteById($params['id']);
        unset($params['id']);
        $this->_repository->edit($note, $params);
        return $note;
    }

    /**
     * 新增/删除/从公开转为私有笔记后同步更新缓存
     *  1. 用户的[公开/私有]笔记数
     *  2. 文章的[公开/私有]笔记数
     *  3. 用户对某文章的[公开/私有]笔记数
     * @param SysNote $note
     * @param int $countNum
     * @return bool|SysNote
     */
    public function syncCounterFromCache($note, $countNum = GlobalCounterConst::INCREASE)
    {
        $noteIsPublic = $note->is_public == GlobalNoteConst::IS_PUBLIC;
        $counterKey = $noteIsPublic ? GlobalCounterConst::COUNT_PUBLIC_NOTE : GlobalCounterConst::COUNT_SELF_NOTE;
        Cache::getInstance([
            'user_id' => $note->user_id
        ])->incrFromArray(CacheKey::COUNTER_USER, $counterKey, $countNum);
        Cache::getInstance([
            'article_id' => $note->article_id,
        ])->incrFromArray(CacheKey::COUNTER_ARTICLE, $counterKey, $countNum);
        Cache::getInstance([
            'user_id' => $note->user_id,
            'article_id' => $note->article_id,
        ])->incrFromArray(CacheKey::COUNTER_USER_ARTICLE, $counterKey, $countNum);
        return true;
    }

    /**
     * 新增/删除/从公开转为私密笔记后记录日志
     * @param SysNote $note
     * @param $receiveUserId
     * @param bool $isDel [是否为删除操作]
     * @return bool
     */
    public function saveNoteLogCounterByQueue($note, $receiveUserId, $isDel = false)
    {
        $noteIsPublic = $note->is_public == GlobalNoteConst::IS_PUBLIC;
        $actionType = $noteIsPublic ? GlobalCounterConst::ACTION_TYPE_PUBLIC_NOTE : GlobalCounterConst::ACTION_TYPE_SELF_NOTE;
        if ($isDel) {
            $actionType = $noteIsPublic ? GlobalCounterConst::ACTION_TYPE_DEL_PUBLIC_NOTE : GlobalCounterConst::ACTION_TYPE_DEL_SELF_NOTE;
        }
        $logData = [
            'send_user_id' => $note->user_id,
            'receive_user_id' => $receiveUserId,
            'action_id' => $note->note_id,
            'action_type' => $actionType,
            'target_id' => $note->article_id,
            'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
            'created_at' => time(),
        ];
        $this->LogCountService->createLogByQueue($logData);
        return true;
    }

    /**
     * 公开转私密后，一下为队列操作
     *  1.[sys_dynamic_]动态的可见性is_view = 0
     *  2.[user_attention_]关注动态的动态可见性is_view = 0
     *  3.[feed_dynamic]可见性is_view = 0
     *  4.[user_message_]删除非笔记发布者的【我发出的评论消息】
     * @param SysNote $note
     * @return \Illuminate\Foundation\Bus\PendingDispatch
     */
    public function ifSelfNotedelDynamicAndMessage($note)
    {
        $queueData = [
            'note_id' => $note->note_id,
            'user_id' => $note->user_id,
        ];
        return $this->addToQueue($queueData);
    }

    /**
     * @param $noteId
     * @param $currentUserId
     * @return SysNote|bool|\Illuminate\Database\Eloquent\Builder|object|null
     */
    public function canDelNote($noteId, $currentUserId)
    {
        $note = $this->_repository->getNoteById($noteId);
        if (!$note) {
            return false;
        }
        if ($note->is_deleted == GlobalConst::IS_DELETED && $note->is_view == GlobalNoteConst::IS_NOT_VIEW) {
            return false;
        }
        if ($note->user_id != $currentUserId) {
            return false;
        }

        return $note;
    }

    /**
     * @param $userId
     * @param $articleId
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public function getLineNotesByUserIdAndArticleId($userId, $articleId)
    {
        return $this->_repository->getLineNotesByUserIdAndArticleId($userId, $articleId);
    }

    /**
     * @param $noteId
     * @param array $selects
     * @return bool|SysNote
     */
    public function getNote($noteId, $selects = ['*'])
    {
        $selects = $selects ?: NoteSelect::getSelects(NoteSelect::SELF_DETAIL);
        if (empty($this->_note[$noteId])) {
            $note = $this->_repository->getNoteById($noteId, $selects);
            if (!$note) {
                return false;
            }
            $this->_note[$noteId] = $note;
        }
        return $this->_note[$noteId];
    }

    /**
     * @param SysNote $note
     * @return array
     */
    public function getNoteMoreFields($note)
    {
        $tableCount = IdBuilder::getSubTable($note->note_id);
        $noteContent = repository()->NoteRepository->getNoteContentByTableCount($tableCount, $note->note_id);
        $noteSelected = repository()->NoteRepository->getNoteSelectedByTableCount($tableCount, $note->note_id);
        $noteLine = repository()->NoteRepository->getNoteLineByTableCount($tableCount, $note->note_id);
        $noteImg = repository()->NoteRepository->getNoteImgByTableCount($tableCount, $note->note_id);
        $user = repository()->UserRepository->getUserByUserId($note->user_id);

        // 话题
        $topicArr = TopicFormat::getTargetTopicArr($note->note_id);

        return ArrayHelper::merge($note->commonFormat(), [
            'content' => $noteContent ? $noteContent[0]['content'] : '',
            'selected' => $noteSelected->toArray() ? $noteSelected[0]['selected'] : '',
            'image' => isset($noteImg[0]) ? ArrayHelper::getColumn(Json::decode($noteImg[0]->url, true), 'url') : [],
            'start_index' => $noteLine->toArray() ? $noteLine[0]['start_index'] : 0,
            'end_index' => $noteLine->toArray() ? $noteLine[0]['end_index'] : 0,
            'user' => $user->commonFormat(UserField::NOTE),
            'topic' => $topicArr[$note->note_id] ?? [],
            'article' => $note->type == GlobalNoteConst::TYPE_ESSAY_NOTE ? (object)[] : $note->article->commonFormat(ArticleField::NOTE),
            'count' => $this->CounterService->getTargetCount(CacheKey::COUNTER_NOTE, [
                'collect',
                'like',
                'comment',
            ], ['note_id' => $note->note_id]),
        ]);
    }

    /**
     * @title 获取单个笔记信息
     * @param string $noteId
     * @param array $returnField 返回字段
     * @return array|mixed|null
     */
    public function getNoteOne($noteId, $returnField = ['note_id', 'is_deleted', 'is_shield', 'type', 'article_id'])
    {
        $noteModel = SysNote::getInstance($noteId)->select($returnField)->where('note_id', $noteId)->first();
        $note = ArrayHelper::toArray($noteModel);
        if (!empty($note) && isset($note['is_deleted']) && isset($note['is_shield'])) {
            //获取文章异常状态
            $note['is_fail'] = $this->getNoteFail($note['is_deleted'], $note['is_shield']);
            $note['id'] = $note['note_id'];
            $note = ArrayHelper::remove($note, ['is_shield', 'is_deleted']);
        }
        $note['content'] = $this->getNoteContent($note['note_id']);
        //获取笔记图片
        $note['img'] = [];
        if ($note['type'] == GlobalNoteConst::TYPE_ESSAY_NOTE) {
            $noteImg = $this->getOne($note['id']);
            if (!empty($noteImg['url'])) {
                $note['img'] = json_decode($noteImg['url']);
            }
        }
        //获取文章信息
        $article = (object)[];
        if (!empty($note['article_id'])) {
            $article = !$noteModel->article ? [] : $noteModel->article->commonFormat(ArticleField::MESSAGE_STATIC);
        }
        $note['article'] = $article;
        return $note;
    }

    /**
     * @title 获取笔记异常状态
     * @param string $isDelete
     * @param string $isShield
     * @return bool
     */
    public function getNoteFail($isDelete, $isShield)
    {
        //验证文章异常状态
        if ($isDelete == GlobalConst::IS_DELETED || $isShield == GlobalConst::IS_SHIELD) {
            return true;
        }
        return false;
    }

    /**
     * 获取单个笔记content数据
     * @param string $id 笔记id
     * @return SysNoteContent|array|null|string
     */
    public function getNoteContent($id)
    {
        //获取笔记content
        $model = new SysNoteContent();
        $sysNoteContent = SysNoteContent::getModel($id)
            ->select($model->fields['detail'])
            ->where(['note_id' => $id])
            ->first();
        $data = !empty($sysNoteContent) ? $sysNoteContent->content : '';
        return $data;
    }

    /**
     * 获取单个笔记的图片
     * @param array $id 笔记id
     * @param string $returnField
     * @return SysNoteImg|array|null|string
     */
    public function getOne($id, string $returnField = 'getDetail')
    {
        $model = new SysNoteImg();
        $sysNoteSelected = SysNoteImg::getModel($id)
            ->select($model->fields[$returnField])
            ->where(['note_id' => $id])
            ->first();
        return $sysNoteSelected;
    }

    /**
     * @param $data
     * @return \Illuminate\Foundation\Bus\PendingDispatch
     */
    public function addToQueue($data)
    {
        return Queue::getInstance(NoteJob::class)
            ->push($data, GlobalQueueConst::NOTE_SELF);
    }
}
